package com.geline.easyexcel.manager;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.geline.easyexcel.annotation.ExcelExportList;
import com.geline.easyexcel.annotation.ExcelExportPage;
import com.geline.easyexcel.annotation.ExcelImport;
import com.geline.easyexcel.config.EasyExcelRegistrar;
import com.geline.easyexcel.domain.*;
import com.geline.easyexcel.handler.CommentWriteHandler;
import com.geline.easyexcel.interfaces.ExcelCrudConsumer;
import com.geline.easyexcel.listener.JdbcEventListener;
import com.geline.easyexcel.utils.EasyExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * Excel导入解析类
 *
 * @author: mengxin
 * @date: 2023/12/29 17:08
 */
@Slf4j
public class EasyExcelManager {

    private EasyExcelProperties properties;
    private DynamicSqlManager dynamicSqlManager;
    private ExcelCrudConsumer excelConsumer;
    private String password;

    public EasyExcelManager(EasyExcelProperties properties, DynamicSqlManager manager, ExcelCrudConsumer excelConsumer){
        this.properties = properties;
        this.dynamicSqlManager = manager;
        this.excelConsumer = excelConsumer;
        this.password = properties.getPassword();
    }

    /**
     * 导出分页查询Excel模板
     * 带下拉选项(支持下拉单选，下拉多选，级联下拉)
     * @param response
     * @param reportCode
     */
    public void exportPageTemplate(HttpServletResponse response, String reportCode, boolean readJar, String title) {
        ExcelExportPage excelExport = EasyExcelRegistrar.getExcelExportPage(reportCode);
        Assert.notNull(excelExport, String.format("can not find @ExcelExportPage by %s !", reportCode));
        Class rowClass = EasyExcelRegistrar.getExportRowClassByPage(reportCode);
        List list = new ArrayList();
        String template = excelExport.templateFile();
        InputStream inputStream = null;
        if(readJar){
            inputStream = this.getClass().getClassLoader().getResourceAsStream(template);
        }else {
            try {
                File file = ResourceUtils.getFile(template);
                inputStream = IoUtil.toStream(file);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        String name = StrUtil.isBlank(title) ? "模版" : title;
        EasyExcelUtils.writeSelectedSheet(response, name, list, rowClass, null, inputStream, this.password);
    }

    /**
     * 导出统计报表Excel模板
     * 带下拉选项(支持下拉单选，下拉多选，级联下拉)
     * @param response
     * @param reportCode
     * @param example 查询参数
     */
    public void exportListTemplate(HttpServletResponse response, String reportCode, Map<String, Object> example, boolean readJar, String title) {
        ExcelExportList excelExport = EasyExcelRegistrar.getExcelExportList(reportCode);
        Assert.notNull(excelExport, String.format("can not find @ExcelExportList by %s !", reportCode));
        Class rowClass = EasyExcelRegistrar.getExportRowClassByList(reportCode);
        List list = new ArrayList();
        Map<String, String> variableMap = dynamicSqlManager.getVariableMap(reportCode, example);
        String template = excelExport.templateFile();
        InputStream inputStream = null;
        if(readJar){
            inputStream = this.getClass().getClassLoader().getResourceAsStream(template);
        }else {
            try {
                File file = ResourceUtils.getFile(template);
                inputStream = IoUtil.toStream(file);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        String name = StrUtil.isBlank(title) ? "模版" : title;
        EasyExcelUtils.writeSelectedSheet(response, name, list, rowClass, variableMap, inputStream, this.password);
    }

    /**
     * 导出分页查询Excel数据
     * 带下拉选项(支持下拉单选，下拉多选，级联下拉)
     * @param response
     * @param reportCode
     * @param list
     */
    public void exportPageExcel(HttpServletResponse response, String reportCode, List list, boolean readJar, String title) {
        ExcelExportPage excelExport = EasyExcelRegistrar.getExcelExportPage(reportCode);
        Assert.notNull(excelExport, String.format("can not find @ExcelExportPage by %s !", reportCode));
        Class rowClass = EasyExcelRegistrar.getExportRowClassByPage(reportCode);
        String template = excelExport.templateFile();
        InputStream inputStream = null;
        if(readJar){
            inputStream = this.getClass().getClassLoader().getResourceAsStream(template);
        }else {
            try {
                File file = ResourceUtils.getFile(template);
                inputStream = IoUtil.toStream(file);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        String name = StrUtil.isBlank(title) ? "excel" : title;
        EasyExcelUtils.writeSelectedSheet(response, name, list, rowClass, null, inputStream, this.password);
    }

    /**
     * 导出统计报表Excel数据
     * 带下拉选项(支持下拉单选，下拉多选，级联下拉)
     * @param response
     * @param reportCode
     * @param example 查询参数
     */
    public void exportListExcel(HttpServletResponse response, String reportCode, Map<String, Object> example, boolean readJar, String title) {
        ReportResult result = getReportResult(reportCode, example);
        this.exportListExcel(response, reportCode, result, readJar, title);
    }

    /**
     * 导出统计报表Excel数据
     * 带下拉选项(支持下拉单选，下拉多选，级联下拉)
     * @param response
     * @param reportCode
     * @param result
     */
    public void exportListExcel(HttpServletResponse response, String reportCode, ReportResult result, boolean readJar, String title) {
        //获取模板全局替换变量
        Map<String, String> variableMap = result.getVariableMap();
        //获取列表数据
        List list = result.getList();
        ExcelExportList exportList = EasyExcelRegistrar.getExcelExportList(reportCode);
        Class rowClass = EasyExcelRegistrar.getExportRowClassByList(reportCode);
        String template = exportList.templateFile();
        InputStream inputStream = null;
        if(readJar){
            inputStream = this.getClass().getClassLoader().getResourceAsStream(template);
        }else {
            try {
                File file = ResourceUtils.getFile(template);
                inputStream = IoUtil.toStream(file);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        String name = StrUtil.isBlank(title) ? "excel" : title;
        EasyExcelUtils.writeSelectedSheet(response, name, list, rowClass, variableMap, inputStream, this.password);
    }

    /**
     * 查询Excel报表数据
     * @param reportCode
     * @param example
     * @return
     */
    public ReportResult getReportResult(String reportCode, Map<String, Object> example) {
        ExcelExportList exportList = EasyExcelRegistrar.getExcelExportList(reportCode);
        Assert.notNull(exportList, String.format("can not find @ExcelExportList by %s !", reportCode));
        Class rowClass = EasyExcelRegistrar.getExportRowClassByList(reportCode);
        List data = dynamicSqlManager.listData(reportCode, example, rowClass);
        //处理数据：如字典翻译
        List list = excelConsumer.handleReportData(data);
        //获取模板全局替换变量
        Map<String, String> variableMap = dynamicSqlManager.getVariableMap(reportCode, example);
        ReportResult result = new ReportResult();
        result.setList(list);
        result.setVariableMap(variableMap);
        return result;
    }

    /**
     * 解析excel
     */
    public <T> JdbcEventListener<T> parseExcel(String reportCode, InputStream inputStream, Map<String, Object> example) {
        Class rowClass = EasyExcelRegistrar.getImportRowClass(reportCode);
        Assert.notNull(rowClass, String.format("can not find : %s !", reportCode));
        ExcelImport annotation = EasyExcelRegistrar.getExcelImport(reportCode);
        Assert.notNull(annotation, String.format("can not find @ExcelImport by %s !", reportCode));
        int rowNumber = annotation.headRowNumber();
        JdbcEventListener listener = new JdbcEventListener(rowClass, (Consumer<Collection>)excelConsumer::saveBatch, example, excelConsumer.getValidFunction());
        EasyExcel.read(inputStream, rowClass, listener).headRowNumber(rowNumber).sheet().doRead();
        return listener;
    }

    /**
     * 解析excel并写入异常信息到缓存
     * @return
     */
    public String parseExcelAndSaveError(String reportCode, MultipartFile multipartFile, Map<String, Object> example) throws IOException {
        Class rowClass = EasyExcelRegistrar.getImportRowClass(reportCode);
        Assert.notNull(rowClass, String.format("can not find excelModel : %s !", reportCode));

        ExcelImport annotation = EasyExcelRegistrar.getExcelImport(reportCode);
        Assert.notNull(annotation, String.format("can not find @ExcelImport by %s !", reportCode));

        //删除过期Excel文件
        excelConsumer.deleteExcelFileByExpire(properties);

        int rowNumber = annotation.headRowNumber();
        JdbcEventListener listener = new JdbcEventListener(rowClass, (Consumer<Collection>)excelConsumer::saveBatch, example, excelConsumer.getValidFunction());

        InputStream inputStream = multipartFile.getInputStream();
        EasyExcel.read(inputStream, rowClass, listener).headRowNumber(rowNumber).sheet().doRead();

        if (listener.getExcelErrorMap().size()>0){
            String filename = multipartFile.getOriginalFilename();
            byte[] bytes = multipartFile.getBytes();
            ExcelParseParam result = excelConsumer.saveExcelFile(listener.getExcelErrorMap(), properties, filename, bytes);
            return result.getFileId();
        }
        return null;
    }

    /**
     * 下载失败Excel加批注提示
     *
     * @param response
     * @param reportCode
     * @param errorId
     */
    public void downErrorExcel(HttpServletResponse response, String reportCode, String errorId) throws IOException {
        //封装response头
        response.reset();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("Excel数据异常反馈.xlsx", "UTF-8"));
        this.downErrorExcel(response.getOutputStream(), reportCode, errorId);
    }

    public void downErrorExcel(OutputStream outputStream, String reportCode, String errorId) {
        ExcelExportPage excelExport = EasyExcelRegistrar.getExcelExportPage(reportCode);
        Assert.notNull(excelExport, String.format("can not find @ExcelExport by %s !", reportCode));

        ExcelParseResult result = excelConsumer.getExcelParseResult(errorId);
        if(result==null){
            throw new RuntimeException("文件不存在:"+errorId);
        }

        //构建 ExcelWriter
        Map<String, List<ExcelError>> errorMap = result.getExcelErrorMap();
        ByteArrayInputStream inputStream = IoUtil.toStream(result.getBytes());
        // 生成表格数据
        List<List<Object>> dataList = new ArrayList<>();
        dataList.add(new ArrayList<>());
        ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream)
                .inMemory(Boolean.TRUE).registerWriteHandler(new CommentWriteHandler(errorMap)).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
        excelWriter.write(dataList, writeSheet);
        //千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
    }
}
